#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <alsa/asoundlib.h>

#include "minimp3.h"  // 引入 minimp3 解码库

// 音频播放相关全局变量（ALSA）
static snd_pcm_t *pcm_handle;
static int sample_rate = 44100;  // MP3 常见采样率，解码后会自动修正
static int channels = 2;         // 默认立体声，解码后自动修正

// 初始化 ALSA 音频设备（根据解码后的 PCM 参数配置）
int init_alsa(int target_sample_rate, int target_channels) {
    int rc;
    snd_pcm_hw_params_t *params;
    unsigned int val;
    int dir;

    // 打开默认播放设备（播放流）
    if ((rc = snd_pcm_open(&pcm_handle, "default", SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
        fprintf(stderr, "ALSA: 无法打开设备: %s\n", snd_strerror(rc));
        return -1;
    }

    // 分配参数对象
    snd_pcm_hw_params_alloca(&params);
    snd_pcm_hw_params_any(pcm_handle, params);

    // 设置音频参数（必须与解码后的 PCM 匹配）
    rc = snd_pcm_hw_params_set_access(pcm_handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
    rc |= snd_pcm_hw_params_set_format(pcm_handle, params, SND_PCM_FORMAT_S16_LE);  // 16位PCM
    rc |= snd_pcm_hw_params_set_channels(pcm_handle, params, target_channels);
    if (rc < 0) {
        fprintf(stderr, "ALSA: 无法设置基础参数: %s\n", snd_strerror(rc));
        return -1;
    }

    // 设置采样率（允许接近值，dir 为实际与目标的偏差方向）
    val = target_sample_rate;
    if ((rc = snd_pcm_hw_params_set_rate_near(pcm_handle, params, &val, &dir)) < 0) {
        fprintf(stderr, "ALSA: 无法设置采样率: %s\n", snd_strerror(rc));
        return -1;
    }
    if (val != target_sample_rate) {
        fprintf(stdout, "ALSA: 采样率调整为 %u (目标 %d)\n", val, target_sample_rate);
    }

    // 应用参数到设备
    if ((rc = snd_pcm_hw_params(pcm_handle, params)) < 0) {
        fprintf(stderr, "ALSA: 无法应用参数: %s\n", snd_strerror(rc));
        return -1;
    }

    sample_rate = val;
    channels = target_channels;
    return 0;
}

// 播放 PCM 数据（ALSA）
void play_pcm(int16_t *pcm_data, int pcm_frames) {
    int rc;
    // snd_pcm_writei 接收“帧数”，1帧 = 声道数 × 采样点（16位=2字节）
    rc = snd_pcm_writei(pcm_handle, pcm_data, pcm_frames);

    if (rc < 0) {
        // 尝试恢复设备（如设备被占用）
        rc = snd_pcm_recover(pcm_handle, rc, 0);
    }
    if (rc < 0) {
        fprintf(stderr, "ALSA: 播放失败: %s\n", snd_strerror(rc));
    }
}

// 用 Socket 发起 HTTP GET 请求，拉取 MP3 流（返回 socket 句柄，失败返回 -1）
int http_get_stream(const char *ip, int port, const char *path) {
    int sockfd;
    struct sockaddr_in serv_addr;
    char http_req[512];

    // 1. 创建 TCP socket
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("Socket: 创建失败");
        return -1;
    }

    // 2. 配置服务器地址
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(port);  // 端口转网络字节序（大端）
    // IP 地址转网络字节序
    if (inet_pton(AF_INET, ip, &serv_addr.sin_addr) <= 0) {
        perror("Socket: 无效 IP 地址");
        close(sockfd);
        return -1;
    }

    // 3. 连接服务器
    if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
        perror("Socket: 连接失败（检查 IP/端口/防火墙）");
        close(sockfd);
        return -1;
    }

    // 4. 构造 HTTP GET 请求（获取 MP3 流）
    snprintf(http_req, sizeof(http_req), 
             "GET %s HTTP/1.1\r\n"
             "Host: %s:%d\r\n"
             "Connection: keep-alive\r\n"  // 长连接，持续拉流
             "\r\n",  // 请求头结束标志
             path, ip, port);

    // 5. 发送 HTTP 请求
    if (send(sockfd, http_req, strlen(http_req), 0) < 0) {
        perror("Socket: 发送请求失败");
        close(sockfd);
        return -1;
    }

    // 6. 读取并跳过 HTTP 响应头（只保留 MP3 流数据）
    char buf[1024];
    int total_read = 0;
    char *header_end = NULL;
    // 循环读取，直到找到响应头结束标志 "\r\n\r\n"
    while ((total_read = recv(sockfd, buf, sizeof(buf), 0)) > 0) {
        header_end = strstr(buf, "\r\n\r\n");
        if (header_end != NULL) {
            // 计算 MP3 流数据的起始位置（跳过响应头）
            int stream_start = header_end - buf + 4;  // 4 = "\r\n\r\n" 长度
            // 若当前缓冲区有剩余 MP3 数据，先保留到缓冲区开头（后续解码用）
            if (stream_start < total_read) {
                memmove(buf, buf + stream_start, total_read - stream_start);
            }
            // 返回 socket 句柄，此时 buf 中已包含部分 MP3 流数据
            return sockfd;
        }
        // 若未找到响应头，继续读取（小概率响应头超过 1024 字节，可扩大 buf 解决）
    }

    fprintf(stderr, "Socket: 未找到 HTTP 响应头\n");
    close(sockfd);
    return -1;
}

int main(int argc, char *argv[]) {
    // 输入参数检查（需传入：IP 端口 路径，如 ./player 192.168.234.1 8080 /ljh）
    if (argc != 4) {
        fprintf(stderr, "用法: %s <VLC_IP> <VLC_PORT> <STREAM_PATH>\n", argv[0]);
        fprintf(stderr, "示例: %s 192.168.234.1 8080 /ljh\n", argv[0]);
        return 1;
    }
    const char *vlc_ip = argv[1];
    int vlc_port = atoi(argv[2]);
    const char *stream_path = argv[3];

    // 1. 初始化 Socket 并拉取 HTTP 流（跳过响应头）
    int sockfd = http_get_stream(vlc_ip, vlc_port, stream_path);
    if (sockfd < 0) {
        fprintf(stderr, "初始化 HTTP 流失败\n");
        return 1;
    }
    fprintf(stdout, "已连接到 %s:%d%s，开始接收流...\n", vlc_ip, vlc_port, stream_path);

    // 2. 初始化 minimp3 解码器
    mp3_decoder decoder;
    mp3_info info;
    mp3_init(&decoder);
    int16_t pcm_buffer[MINIMP3_MAX_SAMPLES_PER_FRAME * 2];  // 解码输出缓冲区（支持立体声）
    int pcm_frames;  // 每次解码得到的 PCM 帧数

    // 3. 读取 Socket 流数据并解码（循环）
    char mp3_buf[4096];  // MP3 流接收缓冲区
    int mp3_buf_len = 0;  // 当前缓冲区中未解码的 MP3 数据长度
    int recv_len;         // 每次从 Socket 接收的字节数

    // 首次读取：HTTP 响应头后，buf 中已残留部分 MP3 数据（从 http_get_stream 继承）
    mp3_buf_len = strlen(mp3_buf);  // 继承之前的残留数据长度

    while (1) {
        // 3.1 从 Socket 接收 MP3 流数据（填满缓冲区）
        recv_len = recv(sockfd, mp3_buf + mp3_buf_len, sizeof(mp3_buf) - mp3_buf_len, 0);
        if (recv_len <= 0) {
            if (recv_len < 0) perror("Socket: 接收失败");
            else fprintf(stdout, "Socket: 流结束\n");
            break;
        }
        mp3_buf_len += recv_len;

        // 3.2 用 minimp3 解码 MP3 数据（一次解码尽可能多的帧）
        while (mp3_buf_len > 0) {
            pcm_frames = mp3_decode(&decoder, (const uint8_t *)mp3_buf, mp3_buf_len, pcm_buffer, &info);

            // 解码结果处理
            if (pcm_frames > 0) {
                // 首次解码成功：获取 MP3 实际参数，初始化 ALSA
                static int alsa_inited = 0;
                if (!alsa_inited) {
                    if (init_alsa(info.hz, info.channels) != 0) {
                        close(sockfd);
                        return 1;
                    }
                    alsa_inited = 1;
                    fprintf(stdout, "解码参数: 采样率 %d Hz, 声道数 %d\n", info.hz, info.channels);
                }

                // 播放解码后的 PCM 数据
                play_pcm(pcm_buffer, pcm_frames);
            }

            // 3.3 移动未解码的 MP3 数据到缓冲区开头（丢弃已解码部分）
            if (decoder.pos > 0) {
                memmove(mp3_buf, mp3_buf + decoder.pos, mp3_buf_len - decoder.pos);
                mp3_buf_len -= decoder.pos;
                decoder.pos = 0;
            } else {
                // 无数据可解码（需继续接收），退出内层循环
                break;
            }
        }
    }

    // 4. 清理资源
    snd_pcm_drain(pcm_handle);  // 播放完剩余数据
    snd_pcm_close(pcm_handle);
    mp3_exit(&decoder);
    close(sockfd);
    fprintf(stdout, "程序正常退出\n");

    return 0;
}